﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Data;
namespace cpktools
{
    class CpkContent
    {
        BinaryReader br;
        long HeadLength;
        long TocLength,TocOffset;
        long ITocLength,ITocOffset;
        long ETocLength,ETocOffset;
        XorCrypt Crypter;//异或变化操作
        DirectoryInfo dir;
        FileInfo fi;
        UTFTable utb;
        UTFTable itoc,toc,etoc;
        private static byte[] HeadExpect={0x40,0x55,0x54,0x46};
        public CpkContent(FileStream fs)
        {
            br = new BinaryReader(fs);
            Crypter = new XorCrypt(0x5f, 0x15);
            fi=new FileInfo(fs.Name);
            dir=fi.Directory;
        }
        ~CpkContent()
        {
            br.Close();
        }
        public void OutputHead()
        {
            utb=GetHead(0,ref HeadLength,"CPK ");
            //utb.Output();
            TocOffset = Convert.ToInt32(utb.GetColumn("TocOffset")[0]);
            ITocOffset = Convert.ToInt32(utb.GetColumn("ITocOffset")[0]);
            ETocOffset = Convert.ToInt32(utb.GetColumn("ETocOffset")[0]);
            //GetDecUTFTable((int)TocOffset, "toc.bin");
            //GetDecUTFTable((int)ITocOffset, "itoc.bin");
            //GetDecUTFTable((int)ETocOffset, "etoc.bin");
            toc = GetHead(TocOffset, ref TocLength, "TOC ");
            itoc = GetHead(ITocOffset, ref ITocLength, "ITOC");
            etoc = GetHead(ETocOffset, ref ETocLength,"ETOC");
            long content = Convert.ToInt64(utb.GetTable.Rows[0]["ContentOffset"]);
            foreach (DataRow dr in toc.GetTable.Rows)
            {
                //GetSubFile(Convert.ToInt64(dr["FileOffset"])+0x800,Convert.ToInt32(dr["FileSize"]),(string)dr["FileName"]);
            }
            OutputTest(utb.PackTable(),"new_head");
            OutputTest(toc.PackTable(), "new_toc");
            Console.WriteLine("Head has been outputted");
        }

        private void GetDecUTFTable(int tocoffset,string name)
        {
            br.BaseStream.Seek(tocoffset + 8, SeekOrigin.Begin);
            TocLength = br.ReadInt64() + 4;
            if (br.BaseStream.Position + TocLength > br.BaseStream.Length) TocLength -= 4;
            byte[] buf = Crypter.XorRead(br.BaseStream, (int)TocLength);
            FileStream fw = new FileStream(Path.Combine(dir.FullName, name), FileMode.Create, FileAccess.Write);
            fw.Write(buf, 0, buf.Length);
            fw.Close();
        }
        private void OutputTest(byte[] bytes, string name)
        {
            FileStream fw = new FileStream(Path.Combine(dir.FullName, name), FileMode.Create, FileAccess.Write);
            fw.Write(bytes, 0, bytes.Length);
            fw.Close();
        }
        private void GetSubFile(long offset, int length, string name)
        {
            DirectoryInfo subdir = new DirectoryInfo(Path.Combine(dir.FullName, fi.Name + "_ex"));
            if (!subdir.Exists)
                subdir.Create();
            FileInfo subfile = new FileInfo(Path.Combine(subdir.FullName, name));
            FileStream fs = subfile.Create();
            byte[] buf = new byte[0x800];
            br.BaseStream.Seek(offset,SeekOrigin.Begin);
            long pos = offset + length;
            while (br.BaseStream.Position < pos)
            {
                int cnt = (int)Math.Min(0x800, pos-br.BaseStream.Position);
                br.Read(buf, 0, cnt);
                fs.Write(buf, 0, cnt);
            }
            Console.WriteLine(String.Format("{0} has been outputted", name));
            fs.Close();
        }
        void init()
        {
            br.BaseStream.Seek(0x8, SeekOrigin.Begin);
            HeadLength = br.ReadInt64() + 4;
        }
        
        UTFTable GetHead(long offset,ref long length, string id)
        {
            byte[] buf;
            br.BaseStream.Seek(offset, SeekOrigin.Begin);
            char[] rid = ASCIIEncoding.ASCII.GetChars(br.ReadBytes(8));
            if (!isHeadOK(id, rid))
            {
                throw new Exception("文件头找不到"+id);
            }
            UTFTable ret;
            length = br.ReadInt64();
            buf = br.ReadBytes((int)length);
            if (isHeadOK("@UTF", ASCIIEncoding.ASCII.GetChars(buf,0,4)))
                ret = new UTFTable(buf, (int)offset);
            else
            {
                br.BaseStream.Seek(offset+0x10, SeekOrigin.Begin);
                buf = Crypter.XorRead(br.BaseStream, (int)length);
                if (isHeadOK("@UTF",ASCIIEncoding.ASCII.GetChars(buf, 0, 4)))
                    ret = new UTFTable(buf, (int)offset);
                else
                    throw new Exception("文件头找不到@UTF");
            }
            return ret;
        }

        bool isHeadOK(string a,char[] b)
        {
            for (int i = 0; i < a.Length; i++)
            {
                if (a[i] != b[i]) return false;
            }
            return true;
        }


    }
}
